#include "AnimatedSprite.h"
#include "AnimatedSpriteType.h"
#include "BoundingVolume.h"
#include "CollidableObject.h"
#include "Game.h"
#include "GameDataLoader.h"
#include "GameGraphics.h"
#include "GameWorld.h"
#include "PhysicalProperties.h"
#include "TiledLayer.h"
#include "SparseLayer.h"
#include "SpriteManager.h"
#include "Viewport.h"
#include "Bot.h"
#include "GamePhysics.h"
#include "GameAI.h"

wstring s2ws(const std::string& s)
{
	 int len;
	 int slength = (int)s.length() + 1;
	 len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
	 wchar_t* buf = new wchar_t[len];
	 MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
	 std::wstring r(buf);
	 delete[] buf;
	 return r;
}

/*
	loadLevelExample - This method loads the current
	level with data. It illustrates all the data
	we would need to load ourselves. You should
	load your data by reading it from a file.
*/
void loadLevel(Game *game)
{
	// FIRST SETUP THE GAME WORLD DIMENSIONS
	GameWorld *world = game->getWorld();
	GameGraphics *graphics = game->getGraphics();
	GamePhysics *physics = game->getPhysics();

	TextureManager *worldTextureManager = graphics->getWorldTextureManager();
	Viewport *viewport = world->getViewport();
	viewport->setViewportWidth(1024);
	viewport->setViewportHeight(767);
	viewport->setViewportOffsetX(0);
	viewport->setViewportOffsetY(0);
	world->setWorldWidth(WORLD_WIDTH);		
	world->setWorldHeight(WORLD_HEIGHT);
	
	// NOW LOAD A TILED BACKGROUND
	TiledLayer *tiledLayer = new TiledLayer(WORLD_WIDTH/TILE_WIDTH, 
											WORLD_HEIGHT/TILE_HEIGHT, 
											TILE_WIDTH, TILE_HEIGHT, 
											0, false, viewport, 
											WORLD_WIDTH, WORLD_HEIGHT);
	
	
	int tileImageIDList[50];	
	
	ifstream inFile ("design/DataFile.txt");
    string line;
    
	// Read level files
	getline (inFile, line);	// First line tells how many level file
	wstring levelFiles[10];	// Assuming only 10 levels maximum
	int size = atoi(line.c_str());
	for(int i = 0; i < size; i++)
	{
		getline (inFile, line);
		levelFiles[i] = s2ws(line);
	}
              
	// Read tile files
	getline (inFile, line);	// Read how many tile files
	wstring tileFiles[50];	// Assuming only 50 tiles maximum (18 tiles + 25 sprites + 7 guards)
	size = atoi(line.c_str());
	wchar_t *fileName;
	for(int i = 0; i < size; i++)	// 18 tile files in total
	{
		getline (inFile, line);
		
		LPCWSTR temp;
		tileFiles[i] = s2ws(line);
		temp = tileFiles[i].c_str();
				
		fileName = constructEmptyWCHAR_TArray(temp);
		tileImageIDList[i] = worldTextureManager->loadTexture(fileName);
	}

	

	// Read csv file to display tiles
	ifstream levelFile (levelFiles[0]);			// Only level 1 file is loaded right now
    int linenum = 0;

	
	PhysicalProperties *tempPlayerProps = new PhysicalProperties();	
	tempPlayerProps ->setX(2.5	* TILE_WIDTH);
	tempPlayerProps ->setY(9	* TILE_HEIGHT);
	tempPlayerProps ->setAccelerationX(0);
	tempPlayerProps ->setAccelerationY(0);
	
	CollidableObject *playerObj = new CollidableObject();
	playerObj->setCurrentlyCollidable(true);
	playerObj->setPhysicalProperties(tempPlayerProps);
	

    while (getline (levelFile, line))
    {		        
        istringstream linestream(line);
        string item;
        int itemnum = 0;
        while (getline (linestream, item, ','))
        {            
			Tile *tileToAdd = new Tile();
			
			PhysicalProperties *tempTileProps = new PhysicalProperties();	
			tempTileProps ->setX(1.0f * TILE_WIDTH * (itemnum++%80));
			tempTileProps ->setY(linenum * TILE_HEIGHT * 1.0f);
			tempTileProps ->setAccelerationX(0);
			tempTileProps ->setAccelerationY(0);
			
			CollidableObject *aObj = new CollidableObject();
			aObj->setPhysicalProperties(tempTileProps);
			
			if(atoi(item.c_str())>=13 && atoi(item.c_str())<=17)	// free space and exit door
				aObj->setCurrentlyCollidable(false);
			else
			{
				aObj->setCurrentlyCollidable(true);		
				physics->addCollision(aObj);
			}
			tileToAdd->textureID = atoi(item.c_str());

			tiledLayer->addTile(tileToAdd);			
        }
		linenum++;
    }			
	world->addLayer(tiledLayer);	

	srand(1);
	
	AnimatedSpriteType *ast = new AnimatedSpriteType();
	AnimatedSpriteType *guardAst = new AnimatedSpriteType();

	for(int i = 18; i < 18+25; i++)	// 25 sprite files
	{
		LPCWSTR temp = (tileFiles[i].c_str());
		fileName = constructEmptyWCHAR_TArray(temp);
		tileImageIDList[i] = worldTextureManager->loadTexture(fileName);
		ast->addImageID(tileImageIDList[i]);
	}
	ast->setAnimationSpeed(15);
	ast->setTextureSize(64, 64);
	
	for(int i = 18+25; i < 18+25+6; i++)	// 25 sprite files
	{
		LPCWSTR temp = (tileFiles[i].c_str());
		fileName = constructEmptyWCHAR_TArray(temp);
		tileImageIDList[i] = worldTextureManager->loadTexture(fileName);
		guardAst->addImageID(tileImageIDList[i]);
	}
	guardAst->setAnimationSpeed(15);
	guardAst->setTextureSize(64, 64);
	

	// Tin sprite

	wchar_t *standingLeftStateName = constructEmptyWCHAR_TArray(L"STANDING_LEFT");
	vector<int> *standingLeftSequence = new vector<int>();
	standingLeftSequence->push_back(tileImageIDList[19]);
	
	wchar_t *standingRightStateName = constructEmptyWCHAR_TArray(L"STANDING_RIGHT");
	vector<int> *standingRightSequence = new vector<int>();
	standingRightSequence->push_back(tileImageIDList[20]);

	wchar_t *runningLeftStateName = constructEmptyWCHAR_TArray(L"RUNNING_LEFT");
	vector<int> *runningLeftSequence = new vector<int>();
	runningLeftSequence->push_back(tileImageIDList[21]);
	runningLeftSequence->push_back(tileImageIDList[22]);
	runningLeftSequence->push_back(tileImageIDList[23]);
	runningLeftSequence->push_back(tileImageIDList[24]);

	wchar_t *runningRightStateName = constructEmptyWCHAR_TArray(L"RUNNING_RIGHT");
	vector<int> *runningRightSequence = new vector<int>();
	runningRightSequence->push_back(tileImageIDList[25]);
	runningRightSequence->push_back(tileImageIDList[26]);
	runningRightSequence->push_back(tileImageIDList[27]);
	runningRightSequence->push_back(tileImageIDList[28]);

	wchar_t *attackingLeftStateName = constructEmptyWCHAR_TArray(L"ATTACK_LEFT");
	vector<int> *attackingLeftSequence = new vector<int>();
	attackingLeftSequence->push_back(tileImageIDList[29]);
	attackingLeftSequence->push_back(tileImageIDList[30]);
	attackingLeftSequence->push_back(tileImageIDList[31]);
	attackingLeftSequence->push_back(tileImageIDList[32]);
	attackingLeftSequence->push_back(tileImageIDList[33]);

	wchar_t *attackingRightStateName = constructEmptyWCHAR_TArray(L"ATTACK_RIGHT");
	vector<int> *attackingRightSequence = new vector<int>();
	attackingRightSequence->push_back(tileImageIDList[34]);
	attackingRightSequence->push_back(tileImageIDList[35]);
	attackingRightSequence->push_back(tileImageIDList[36]);
	attackingRightSequence->push_back(tileImageIDList[37]);
	attackingRightSequence->push_back(tileImageIDList[38]);

	wchar_t *jumpingLeftStateName = constructEmptyWCHAR_TArray(L"JUMP_LEFT");
	vector<int> *jumpingLeftSequence = new vector<int>();
	jumpingLeftSequence->push_back(tileImageIDList[39]);

	wchar_t *jumpingRightStateName = constructEmptyWCHAR_TArray(L"JUMP_RIGHT");
	vector<int> *jumpingRightSequence = new vector<int>();
	jumpingRightSequence->push_back(tileImageIDList[40]);

	wchar_t *jumpingDownStateName = constructEmptyWCHAR_TArray(L"JUMP_DOWN");
	vector<int> *jumpingDownSequence = new vector<int>();
	jumpingDownSequence->push_back(tileImageIDList[41]);

	wchar_t *openingStateName = constructEmptyWCHAR_TArray(L"OPENING");
	vector<int> *openingSequence = new vector<int>();
	openingSequence->push_back(tileImageIDList[42]);

	ast->addAnimationState(standingLeftStateName, standingLeftSequence);
	ast->addAnimationState(standingRightStateName, standingRightSequence);
	ast->addAnimationState(runningLeftStateName, runningLeftSequence);
	ast->addAnimationState(runningRightStateName, runningRightSequence);
	ast->addAnimationState(attackingLeftStateName, attackingLeftSequence);
	ast->addAnimationState(attackingRightStateName, attackingRightSequence);
	ast->addAnimationState(jumpingLeftStateName, jumpingLeftSequence);
	ast->addAnimationState(jumpingRightStateName, jumpingRightSequence);
	ast->addAnimationState(jumpingDownStateName, jumpingDownSequence);
	ast->addAnimationState(openingStateName, openingSequence);

	// guard sprite

	standingLeftStateName = constructEmptyWCHAR_TArray(L"STANDING_LEFT");
	standingLeftSequence = new vector<int>();
	standingLeftSequence->push_back(tileImageIDList[43]);
	
	standingRightStateName = constructEmptyWCHAR_TArray(L"STANDING_RIGHT");
	standingRightSequence = new vector<int>();
	standingRightSequence->push_back(tileImageIDList[44]);

	runningLeftStateName = constructEmptyWCHAR_TArray(L"RUNNING_LEFT");
	runningLeftSequence = new vector<int>();
	runningLeftSequence->push_back(tileImageIDList[45]);
	runningLeftSequence->push_back(tileImageIDList[46]);	

	runningRightStateName = constructEmptyWCHAR_TArray(L"RUNNING_RIGHT");
	runningRightSequence = new vector<int>();
	runningRightSequence->push_back(tileImageIDList[47]);
	runningRightSequence->push_back(tileImageIDList[48]);

	wchar_t *deadRightStateName = constructEmptyWCHAR_TArray(L"GUARD_DEAD");
	vector<int> *deadRightSequence = new vector<int>();
	deadRightSequence->push_back(tileImageIDList[49]);

	guardAst->addAnimationState(standingLeftStateName, standingLeftSequence);
	guardAst->addAnimationState(standingRightStateName, standingRightSequence);
	guardAst->addAnimationState(runningLeftStateName, runningLeftSequence);
	guardAst->addAnimationState(runningRightStateName, runningRightSequence);
	guardAst->addAnimationState(deadRightStateName, deadRightSequence);

	SpriteManager *spriteManager = world->getSpriteManager();
	spriteManager->addSpriteType(ast);
	spriteManager->addSpriteType(guardAst);

	// Tin's pp

	AnimatedSprite *player = new AnimatedSprite();	
	PhysicalProperties *playerProps = new PhysicalProperties();	
	playerProps->setX(2.5	* TILE_WIDTH);
	playerProps->setY(9		* TILE_HEIGHT);
	playerProps->setAccelerationX(0);
	playerProps->setAccelerationY(0);

	player->setPhysicalProperties(playerProps);
	player->setAlpha(255);
	player->setCurrentState(STANDING_RIGHT);	
	player->setSpriteType(ast);
	
	
	// Guard's pp

	AnimatedSprite *guard1 = new AnimatedSprite();	
	PhysicalProperties *guardPp1 = new PhysicalProperties();
	guardPp1->setMass(100);		// To realize it's an AI
	guardPp1->setX(7	* TILE_WIDTH);
	guardPp1->setY(3	* TILE_HEIGHT);
	guardPp1->setAccelerationX(0);
	guardPp1->setAccelerationY(0);
	guard1->setPhysicalProperties(guardPp1);
	guard1->setAlpha(255);
	guard1->setCurrentState(RUNNING_LEFT);			
	guard1->setSpriteType(guardAst);	

	AnimatedSprite *guard2 = new AnimatedSprite();	
	guard2->setSpriteType(guardAst);
	guard2->setAlpha(255);
	guard2->setCurrentState(RUNNING_LEFT);		
	PhysicalProperties *guardPp2 = new PhysicalProperties();
	guard2->setPhysicalProperties(guardPp2);
	guardPp2->setX(25	* TILE_WIDTH);
	guardPp2->setY(3	* TILE_HEIGHT);
	guardPp2->setMass(100);		// To realize it's an AI
	guardPp2->setAccelerationX(0);
	guardPp2->setAccelerationY(0);

	AnimatedSprite *guard3 = new AnimatedSprite();	
	guard3->setSpriteType(guardAst);
	guard3->setAlpha(255);
	guard3->setCurrentState(RUNNING_LEFT);		
	PhysicalProperties *guardPp3 = new PhysicalProperties();
	guard3->setPhysicalProperties(guardPp3);
	guardPp3->setX(40	* TILE_WIDTH);
	guardPp3->setY(9	* TILE_HEIGHT);
	guardPp3->setMass(100);		// To realize it's an AI
	guardPp3->setAccelerationX(0);
	guardPp3->setAccelerationY(0);

	AnimatedSprite *guard4 = new AnimatedSprite();	
	guard4->setSpriteType(guardAst);
	guard4->setAlpha(255);
	guard4->setCurrentState(RUNNING_LEFT);		
	PhysicalProperties *guardPp4 = new PhysicalProperties();
	guard4->setPhysicalProperties(guardPp4);
	guardPp4->setX(56	* TILE_WIDTH);
	guardPp4->setY(9	* TILE_HEIGHT);
	guardPp4->setMass(100);		// To realize it's an AI
	guardPp4->setAccelerationX(0);
	guardPp4->setAccelerationY(0);

	AnimatedSprite *guard5 = new AnimatedSprite();	
	guard5->setSpriteType(guardAst);
	guard5->setAlpha(255);
	guard5->setCurrentState(RUNNING_LEFT);		
	PhysicalProperties *guardPp5 = new PhysicalProperties();
	guard5->setPhysicalProperties(guardPp5);
	guardPp5->setX(67	* TILE_WIDTH);
	guardPp5->setY(6	* TILE_HEIGHT);
	guardPp5->setMass(100);		// To realize it's an AI
	guardPp5->setAccelerationX(0);
	guardPp5->setAccelerationY(0);

	spriteManager->setPlayer(player);
	spriteManager->addSprite(guard1);
	spriteManager->addSprite(guard2);
	spriteManager->addSprite(guard3);
	spriteManager->addSprite(guard4);
	spriteManager->addSprite(guard5);
	
	CollidableObject *aObj1 = new CollidableObject();
	aObj1->setPhysicalProperties(guardPp1);
	aObj1->setCurrentlyCollidable(true);
	CollidableObject *aObj2 = new CollidableObject();
	aObj2->setPhysicalProperties(guardPp2);
	aObj2->setCurrentlyCollidable(true);
	CollidableObject *aObj3 = new CollidableObject();
	aObj3->setPhysicalProperties(guardPp3);
	aObj3->setCurrentlyCollidable(true);
	CollidableObject *aObj4 = new CollidableObject();
	aObj4->setPhysicalProperties(guardPp4);
	aObj4->setCurrentlyCollidable(true);
	CollidableObject *aObj5 = new CollidableObject();
	aObj5->setPhysicalProperties(guardPp5);
	aObj5->setCurrentlyCollidable(true);

//	physics->addCollision(aObj1);
//	physics->addCollision(aObj2);
//	physics->addCollision(aObj3);
//	physics->addCollision(aObj4);
//	physics->addCollision(aObj5);

	Bot *bot1 = new Bot();
	bot1->setAnimatedSprite(guard1);
	Bot *bot2 = new Bot();
	bot2->setAnimatedSprite(guard2);
	Bot *bot3 = new Bot();
	bot3->setAnimatedSprite(guard3);
	Bot *bot4 = new Bot();
	bot4->setAnimatedSprite(guard4);
	Bot *bot5 = new Bot();
	bot5->setAnimatedSprite(guard5);
	
	game->getAI()->addBot(bot1);
	game->getAI()->addBot(bot2);
	game->getAI()->addBot(bot3);
	game->getAI()->addBot(bot4);
	game->getAI()->addBot(bot5);
}


/*
	loadWorld - This method should load the data
	for the GameWorld's current level. The GameWorld
	stores a vector of all level file names. Such
	a file would describe how a level would be
	constructed.
*/
void GameDataLoader::loadWorld(Game *game)	
{
	loadLevel(game);
}

/*
	loadGUI - One could use this method to build
	the GUI based on the contents of a GUI file.
	That way we could change the GUI and add to
	it without having to rebuild the project.
*/
void GameDataLoader::loadGUI(Game *game)
{

}


